
// Copyright (C) 2011-2012 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//  
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Diagnostics;

using Derm.OpenGL;

namespace Derm.Render
{
	/// <summary>
	/// A collection of vertex arrays.
	/// </summary>
	/// <remarks>>
	/// <para>
	/// Possible vertex array configurations:
	/// - Single
	/// </para>
	/// </remarks>
	public class VertexArrayObject : RenderResource
	{
		#region Vertex Array Definition

		/// <summary>
		/// 
		/// </summary>
		/// <param name="semantic"></param>
		/// <returns></returns>
		public bool HasArray(string semantic)
		{
			return (mVertexArrays.ContainsKey(String.Format("##Semantic.{0}", semantic)));
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="semantic"></param>
		/// <returns></returns>
		public ArrayBufferObject GetArray(string semantic)
		{
			if (semantic == null)
				throw new ArgumentNullException("semantic");

			VertexArray vertexArray;

			if (mVertexArrays.TryGetValue(String.Format("##Semantic.{0}", semantic), out vertexArray) == false)
				throw new InvalidOperationException("no array " + semantic);
			
			Debug.Assert(vertexArray != null);

			return (vertexArray.Array);
		}

		/// <summary>
		/// Set an array buffer object to this array.
		/// </summary>
		/// <param name="semantic"></param>
		/// <param name="bufferObject"></param>
		public void SetArray(string semantic, ArrayBufferObject bufferObject)
		{
			SetArray(semantic, "##Semantic", bufferObject, 0);
		}

		/// <summary>
		/// Set an array buffer object to this array.
		/// </summary>
		/// <param name="inputName">
		/// A <see cref="System.String"/> that specifies the name of the array binding.
		/// </param>
		/// <param name="blockName">
		/// A <see cref="System.String"/> that specifies the name of the array binding block. It can be null
		/// for indicating the use of the default group (global scope).
		/// </param>
		/// <param name="bufferObject">
		/// A <see cref="ArrayBufferObject"/> that specifies the contents of the array.
		/// </param>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="inputName"/> is null or is not a valid input name.
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown if <paramref name="bufferObject"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception throw if <paramref name="bufferObject"/> is an interleaved buffer object or it has no items
		/// defined.
		/// </exception>
		public void SetArray(string inputName, string blockName, ArrayBufferObject bufferObject)
		{
			SetArray(inputName, blockName, bufferObject, 0);
		}

		/// <summary>
		/// Set an array buffer object sub-array to this array.
		/// </summary>
		/// <param name="inputName">
		/// A <see cref="System.String"/> that specifies the name of the array binding.
		/// </param>
		/// <param name="blockName">
		/// A <see cref="System.String"/> that specifies the name of the array binding block.
		/// </param>
		/// <param name="bufferObject">
		/// A <see cref="ArrayBufferObject"/> that specifies the contents of the array.
		/// </param>
		/// <param name="fieldIndex">
		/// A <see cref="System.UInt32"/> that specifies the <paramref name="bufferObject"/> sub-array index.
		/// </param>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="inputName"/> is null or is not a valid input name.
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown if <paramref name="bufferObject"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception throw if <paramref name="bufferObject"/> is not an interleaved buffer object.
		/// </exception>
		public void SetArray(string inputName, string blockName, ArrayBufferObject bufferObject, uint fieldIndex)
		{
			if (String.IsNullOrEmpty(inputName))
				throw new ArgumentException("invalid name", "inputName");
			if (bufferObject == null)
				throw new ArgumentNullException("bufferObject");
			if (bufferObject.ItemCount == 0)
				throw new ArgumentException("zero items", "bufferObject");
			if (fieldIndex >= bufferObject.SubArrayCount)
				throw new ArgumentException("out of bounds", "fieldIndex");

			// Map buffer object with input name
			mVertexArrays[inputName] = new VertexArray(bufferObject);
			// Map buffer object with input name including block name also
			if (blockName != null)
				mVertexArrays[String.Format("{0}.{1}", blockName, inputName)] = mVertexArrays[inputName];

			// Set sub-array!
			mVertexArrays[inputName].SubArray = fieldIndex;

			// Compute the actual vertex array length
			UpdateVertexArrayLength();
		}

		/// <summary>
		/// Get an array buffer object collected by this vertex array object.
		/// </summary>
		/// <param name="semantic">
		/// A <see cref="System.String"/> that specify the attribute name.
		/// </param>
		/// <returns>
		/// It returns the array corresponding to the semantic <paramref name="semantic"/>.
		/// </returns>
		private VertexArray GetVertexArray(string semantic)
		{
			return (GetVertexArray(semantic, "##Semantic"));
		}

		/// <summary>
		/// Get an array buffer object collected by this vertex array object.
		/// </summary>
		/// <param name="attributeName">
		/// A <see cref="System.String"/> that specify the attribute name.
		/// </param>
		/// <param name="blockName">
		/// A <see cref="System.String"/> that specify the input block declaring <paramref name="attributeName"/>.
		/// </param>
		/// <returns>
		/// It returns the array corresponding to the attribute having the name <paramref name="attributeName"/>.
		/// </returns>
		private VertexArray GetVertexArray(string attributeName, string blockName)
		{
			if (String.IsNullOrEmpty(attributeName))
				throw new ArgumentException("invalid attribute name", "attributeName");

			VertexArray vertexArray;
			
			if (blockName != null)
				attributeName = String.Format("{0}.{1}", blockName, attributeName);

			if (mVertexArrays.TryGetValue(attributeName, out vertexArray) == false)
				return (null);

			return (vertexArray);
		}

		/// <summary>
		/// The the minimum length of the arrays compositing this vertex array.
		/// </summary>
		private void UpdateVertexArrayLength()
		{
			uint minLength = UInt32.MaxValue;

			foreach (KeyValuePair<string, VertexArray> pair in mVertexArrays)
				minLength = Math.Min(minLength, pair.Value.Array.ItemCount);

			mVertexArrayLength = minLength;
		}

		private void CheckMultiDrawAvailability()
		{
			
		}

		/// <summary>
		/// Validate this vertex array.
		/// </summary>
		/// <returns></returns>
		[Conditional("DEBUG")]
		private void Validate()
		{
			if (mVertexArrays.Count == 0)
				throw new InvalidOperationException("no array");
			if (Elements.Count == 0)
				throw new InvalidOperationException("no element array");
		}

		/// <summary>
		/// A vertex array.
		/// </summary>
		private class VertexArray
		{
			/// <summary>
			/// Construct an VertexArray.
			/// </summary>
			/// <param name="bufferObject">
			/// A <see cref="ArrayBufferObject"/> which defines a vertex array data.
			/// </param>
			public VertexArray(ArrayBufferObject bufferObject)
			{
				if (bufferObject == null)
					throw new ArgumentNullException("bufferObject");
				Array = bufferObject;
			}

			/// <summary>
			/// The vertex array buffer object.
			/// </summary>
			public readonly ArrayBufferObject Array;

			/// <summary>
			/// The vertex array sub-buffer index.
			/// </summary>
			public uint SubArray
			{
				get { return (mSubArray); }
				set
				{
					if (value >= Array.SubArrayCount)
						throw new InvalidOperationException("sub-array index out of bounds");
					mSubArray = value;
				}
			}

			/// <summary>
			/// The vertex array sub-buffer index.
			/// </summary>
			private uint mSubArray;
		}

		/// <summary>
		/// Arrays defined by this vertex array object.
		/// </summary>
		private readonly Dictionary<string, VertexArray> mVertexArrays = new Dictionary<string, VertexArray>();

		/// <summary>
		/// Number of items of the collected buffer objects.
		/// </summary>
		private uint mVertexArrayLength;

		#endregion

		#region Vertex Elements Definition

		/// <summary>
		/// Specify the entire array to be drawn sequentially.
		/// </summary>
		/// <param name="mode">
		/// A <see cref="Primitive"/> that specify how arrays elements are interpreted.
		/// </param>
		public void SetElementArray(Primitive mode)
		{
			// Store element array (entire buffer)
			Elements.Add(new AttributeElements(mode));

			CheckMultiDrawAvailability();
		}

		/// <summary>
		/// Set a buffer object which specifies the element arrays.
		/// </summary>
		/// <param name="mode">
		/// A <see cref="Primitive"/> that specify how arrays elements are interpreted.
		/// </param>
		/// <param name="bufferObject">
		/// A <see cref="ElementBufferObject"/> that specifies a sequence of indices that defines the
		/// array element sequence.
		/// </param>
		public void SetElementArray(Primitive mode, ElementBufferObject bufferObject)
		{
			if (bufferObject == null)
				throw new ArgumentNullException("bufferObject");
			if (bufferObject.BufferType != BufferObjectType.ElementArray)
				throw new ArgumentException("not an ElementBufferObject", "bufferObject");

			// Store element array
			Elements.Add(new AttributeElements(mode, bufferObject));
		}

		/// <summary>
		/// Set a buffer object which specifies the element arrays.
		/// </summary>
		/// <param name="mode">
		/// A <see cref="Primitive"/> that specify how arrays elements are interpreted.
		/// </param>
		/// <param name="bufferObject">
		/// A <see cref="ElementBufferObject"/> that specifies a sequence of indices that defines the
		/// array element sequence.
		/// </param>
		public void SetElementArray(Primitive mode, ElementBufferObject bufferObject, uint offset, uint count)
		{
			if (bufferObject == null)
				throw new ArgumentNullException("bufferObject");
			if (bufferObject.BufferType != BufferObjectType.ElementArray)
				throw new ArgumentException("not an ElementBufferObject", "bufferObject");

			// Store element array
			Elements.Add(new AttributeElements(mode, bufferObject, offset, count));
		}

		/// <summary>
		/// A collection of indices reference input arrays.
		/// </summary>
		internal class AttributeElements
		{
			/// <summary>
			/// Specify how all elements shall be drawn.
			/// </summary>
			/// <param name="mode">
			/// A <see cref="Primitive"/> that indicates how array elements are interpreted.
			/// </param>
			public AttributeElements(Primitive mode) : this(mode, null, 0, 0) { }

			/// <summary>
			/// Specify which elements shall be drawn, specifying an offset and the number of elements.
			/// </summary>
			/// <param name="mode">
			/// A <see cref="Primitive"/> that indicates how array elements are interpreted.
			/// </param>
			/// <param name="offset">
			/// A <see cref="System.UInt32"/> that specify the offset applied to the drawn array elements.
			/// </param>
			/// <param name="count">
			/// A <see cref="System.UInt32"/> that specify the number of array elements drawn.
			/// </param>
			public AttributeElements(Primitive mode, uint offset, uint count) : this(mode, null, offset, count) { }

			/// <summary>
			/// Specify which elements shall be drawn by indexing them.
			/// </summary>
			/// <param name="mode">
			/// A <see cref="Primitive"/> that indicates how array elements are interpreted.
			/// </param>
			/// <param name="indices">
			/// A <see cref="ElementBufferObject"/> containing the indices of the drawn vertices. If it null, no indices are
			/// used for drawing; instead, all array elements are drawns, starting from the first one. If it is not null, all
			/// indices are drawn starting from the first one.
			/// </param>
			public AttributeElements(Primitive mode, ElementBufferObject indices) : this(mode, indices, 0, 0) { }

			/// <summary>
			/// Specify which elements shall be drawn by indexing them, specifying an offset and the number of elements.
			/// </summary>
			/// <param name="mode">
			/// A <see cref="Primitive"/> that indicates how array elements are interpreted.
			/// </param>
			/// <param name="indices">
			/// A <see cref="ElementBufferObject"/> containing the indices of the drawn vertices. If it null, no indices are
			/// used for drawing; instead, <paramref name="count"/> contiguos array elements are drawns, starting from
			/// <paramref name="offset"/>. If it is not null, <paramref name="count"/> indices are drawn starting from
			/// <paramref name="offset"/>.
			/// </param>
			/// <param name="offset">
			/// A <see cref="System.UInt32"/> that specify the offset applied to the drawn array elements.
			/// </param>
			/// <param name="count">
			/// A <see cref="System.UInt32"/> that specify the number of array elements drawn.
			/// </param>
			public AttributeElements(Primitive mode, ElementBufferObject indices, uint offset, uint count)
			{
				ElementsMode = mode;
				ElementOffset = offset;
				ElementCount = count;
				ArrayIndices = indices;
			}

			/// <summary>
			/// The primitive used for interpreting the sequence of the array elements.
			/// </summary>
			public readonly Primitive ElementsMode;

			/// <summary>
			/// The offset for sending element for drawing.
			/// </summary>
			/// <remarks>
			/// <para>
			/// If <see cref="ArrayIndices"/> is null, this member indicates the first index of the array item
			/// to draw, otherwise it indicates the offset (in basic machine unit) of the first element of
			/// <see cref="ArrayIndices"/> to draw.
			/// </para>
			/// </remarks>
			public readonly uint ElementOffset;

			/// <summary>
			/// The number of array elements to draw.
			/// </summary>
			/// <remarks>
			/// <para>
			/// If <see cref="ArrayIndices"/> is null, this member indicates how many array elements are drawn, otherwise
			/// it indicates how many array indices are drawn.
			/// </para>
			/// <para>
			/// In the case this field equals to 0, it means that all array elements shall be drawn.
			/// </para>
			/// </remarks>
			public readonly uint ElementCount;

			/// <summary>
			/// An integral buffer that specify vertices by they index.
			/// </summary>
			public readonly ElementBufferObject ArrayIndices;
		}

		/// <summary>
		/// Collection of elements for drawing arrays.
		/// </summary>
		internal readonly List<AttributeElements>  Elements = new List<AttributeElements>();

		#endregion

		#region Normal Generation

		/// <summary>
		/// 
		/// </summary>
		public void GenerateNormals()
		{
			if (Elements.Count != 1)
				throw new NotSupportedException("multiple elements");
			if (Elements[0].ElementsMode != Primitive.Triangle)
				throw new NotSupportedException("not triangles");

			VertexArray positionArray = GetVertexArray(VertexArraySemantic.Position);
			if (positionArray == null)
				throw new InvalidOperationException("no position semantic");

			ArrayBufferObject positionBuffer = positionArray.Array;
			if (positionBuffer.ArrayType != VertexArrayType.Vec3)
				throw new NotSupportedException("invalid type");

			ArrayBufferObject<Vertex3f> normalBuffer = new ArrayBufferObject<Vertex3f>(BufferObject.Hint.StaticCpuDraw);

			if (Elements[0].ArrayIndices == null) {
				normalBuffer.Define(positionBuffer.ItemCount);

				for (uint i = 0; i < positionBuffer.ItemCount; i += 3) {
					Vertex3f p0 = positionBuffer.GetData<Vertex3f>(i + 0);
					Vertex3f p1 = positionBuffer.GetData<Vertex3f>(i + 1);
					Vertex3f p2 = positionBuffer.GetData<Vertex3f>(i + 2);

					Vertex3f normal = (p1 - p0) ^ (p2 - p0);

					normal.Normalize();

					normalBuffer[i + 0] = normal;
					normalBuffer[i + 1] = normal;
					normalBuffer[i + 2] = normal;
				}
			} else {
				ElementBufferObject elementBuffer = Elements[0].ArrayIndices;

				normalBuffer.Define(positionBuffer.ItemCount);

				// Reset normal buffer (cumulate normals)
				for (uint i = 0; i < positionBuffer.ItemCount; i++)
					normalBuffer[i] = Vertex3f.Zero;

				for (uint i = 0; i < elementBuffer.ItemCount; i += 3) {
					uint p0Index = elementBuffer[i + 0];
					uint p1Index = elementBuffer[i + 1];
					uint p2Index = elementBuffer[i + 2];

					Vertex3f p0 = positionBuffer.GetData<Vertex3f>(p0Index);
					Vertex3f p1 = positionBuffer.GetData<Vertex3f>(p1Index);
					Vertex3f p2 = positionBuffer.GetData<Vertex3f>(p2Index);

					Vertex3f normal = (p1 - p0) ^ (p2 - p0);

					normal.Normalize();

					normalBuffer[p0Index] = normalBuffer[p0Index] + normal;
					normalBuffer[p1Index] = normalBuffer[p1Index] + normal;
					normalBuffer[p2Index] = normalBuffer[p2Index] + normal;
				}

				// Normalize normal buffer
				for (uint i = 0; i < positionBuffer.ItemCount; i++) {
					Vertex3f normal = normalBuffer[i];
					normal.Normalize();
					normalBuffer[i] = normal;
				}
			}

			SetArray(VertexArraySemantic.Normal, normalBuffer);
		}

		#endregion

		#region Vertex Array Application

		/// <summary>
		/// Get or set the <see cref="ShaderProgram"/> bound for drawing this vertex array. It can be null.
		/// </summary>
		public ShaderProgram Shader
		{
			get { return (mShader); }
			set { mShader = value; }
		}

		/// <summary>
		/// Get or set <see cref="RenderStateSet"/> overriden only for this vertex array. It can be null.
		/// </summary>
		public RenderStateSet StateSet
		{
			get { return (mStateSet); }
			set { mStateSet = value; }
		}

		/// <summary>
		/// Render this vertex array.
		/// </summary>
		/// <param name="ctx">
		/// The <see cref="RenderContext"/> used for rendering.
		/// </param>
		/// <param name="shader">
		/// The <see cref="ShaderProgram"/> that is overriden by <see cref="Shader"/> property. This shader is used
		/// only in the case <see cref="Shader"/> is null too.
		/// </param>
		public void DrawVertexArray(RenderContext ctx, ShaderProgram shader)
		{
			if (ctx == null)
				throw new ArgumentNullException("ctx");

			// Default
			if (Shader != null)
				shader = Shader;

			if (shader == null)
				throw new InvalidOperationException("no shader");
			if (shader.IsLinked == false)
				throw new InvalidOperationException("shader not linked");

			// Set vertex arrays
			SetShaderProgramAttributes(ctx, shader);

			// Uses shader
			shader.Bind(ctx);

			// Override render state, if required
			if (mStateSet != null) {
				// Do not alter previous render state
				ctx.PushState();
				ctx.ApplyState(mStateSet, shader);
			}

			// Issue rendering
			foreach (AttributeElements attributeElements in Elements) {
				ElementBufferObject elementIndices = attributeElements.ArrayIndices;

				if (elementIndices == null) {
					uint count = (attributeElements.ElementCount == 0) ? mVertexArrayLength : attributeElements.ElementCount;

					Debug.Assert(count - attributeElements.ElementOffset <= mVertexArrayLength, "element array out of bounds");

					// Draw vertex array sequentially
					Gl.DrawArrays((int) attributeElements.ElementsMode, (int) attributeElements.ElementOffset, (int) count);
					RenderException.DebugCheckErrors();
				} else {
					uint count = (attributeElements.ElementCount == 0) ? elementIndices.ItemCount : attributeElements.ElementCount;

					Debug.Assert(count - attributeElements.ElementOffset <= elementIndices.ItemCount, "element indices array out of bounds");

					// Element array must be bound
					elementIndices.Bind(ctx);
					// Draw vertex arrays by indices
					Gl.DrawElements((int) attributeElements.ElementsMode, (int) count, (int) elementIndices.ItemType, (int) attributeElements.ElementOffset);
					RenderException.DebugCheckErrors();
				}
			}

			// Restore previous render state
			if (mStateSet != null)
				ctx.PopState();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ctx"></param>
		/// <param name="shaderProgram"></param>
		private void SetShaderProgramAttributes(RenderContext ctx, ShaderProgram shaderProgram)
		{
			// Vertex array object supported?
			if (mSupportVertexArray) {
				if (mVertexArrayDirty) {
					CreateName(ctx);	// How to rename an object??? XXX
				}
				
				// Bind this vertex array
				Gl.BindVertexArray(ObjectName);
				RenderException.DebugCheckErrors();

				if (mVertexArrayDirty == false)
					return;		// Vertex attributes configuration already defined
			}

			foreach (string inputName in shaderProgram.ActiveAttributes) {
				VertexArray shaderVertexArray;

				// Get the buffer object containing data for vertex shader attribute
				if ((shaderVertexArray = GetVertexArray(inputName, null)) == null) {

					// Failed, try using attribute semantic, if any

					string semantic = shaderProgram.GetAttributeSemantic(inputName);

					if (semantic != null)
						shaderVertexArray = GetVertexArray(semantic);
				}

				if (shaderVertexArray == null)
					continue;
					//throw new InvalidOperationException("no vertex array for " + inputName);

				ArrayBufferObject.SubArrayBuffer subArrayBuffer = shaderVertexArray.Array.GetSubArrayInfo(shaderVertexArray.SubArray);
				ShaderProgram.AttributeBinding attributeBinding = shaderProgram.GetActiveAttribute(inputName);

				// A single array buffer object could contains multiple sub-array
				shaderVertexArray.Array.Bind(ctx);

				// Bind varying attribute to currently bound buffer object
				if ((ArrayBufferItem.IsFloatBaseType(attributeBinding.Type) == true) || (subArrayBuffer.Normalized == true)) {

					Gl.VertexAttribPointer(
						attributeBinding.Location,
						(int) subArrayBuffer.ArrayLength, (int) subArrayBuffer.BaseType, subArrayBuffer.Normalized,
						(int) subArrayBuffer.ArrayStride, subArrayBuffer.ArrayOffset.ToInt32()
						);
					RenderException.DebugCheckErrors();

				} else {

					Gl.VertexAttribIPointer(
						attributeBinding.Location,
						(int) subArrayBuffer.ArrayLength, (int) subArrayBuffer.BaseType,
						(int) subArrayBuffer.ArrayStride, subArrayBuffer.ArrayOffset.ToInt32()
						);
					RenderException.DebugCheckErrors();

				}

				// Enable vertex attribute
				Gl.EnableVertexAttribArray(attributeBinding.Location);
				RenderException.DebugCheckErrors();
			}

			// Next time do not set inputs
			mVertexArrayDirty = false;
		}

		/// <summary>
		/// The specific shader bound for drawing this vertex array.
		/// </summary>
		private ShaderProgram mShader;

		/// <summary>
		/// The render state overriden only for this vertex array.
		/// </summary>
		private RenderStateSet mStateSet;

		/// <summary>
		/// Flag indicating whether the vertex array is dirty due buffer object changes.
		/// </summary>
		private bool mVertexArrayDirty = true;

		#endregion

		#region RenderResource Overrides

		/// <summary>
		/// Vertex array object class.
		/// </summary>
		internal static readonly Guid VertexArrayObjectClass = new Guid("821E9AC8-6118-4543-B561-7C85BB998287");

		/// <summary>
		/// Vertex array object class.
		/// </summary>
		public override Guid ObjectClass { get { return (VertexArrayObjectClass); } }

		/// <summary>
		/// Determine whether this BufferObject really exists for a specific context.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> that would have created (or a sharing one) the object. This context shall be current to
		/// the calling thread.
		/// </param>
		/// <returns>
		/// It returns a boolean value indicating whether this BufferObject exists in the object space of <paramref name="ctx"/>.
		/// </returns>
		/// <remarks>
		/// <para>
		/// The object existence is done by checking a valid object by its name <see cref="IRenderResource.ObjectName"/>. This routine will test whether
		/// <paramref name="ctx"/> has created this BufferObject (or is sharing with the creator).
		/// </para>
		/// </remarks>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown if <paramref name="ctx"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="ctx"/> is not current to the calling thread.
		/// </exception>
		public override bool Exists(RenderContext ctx)
		{
			if (ctx == null)
				throw new ArgumentNullException("ctx");
			if (ctx.IsCurrent() == false)
				throw new ArgumentException("not current", "ctx");

			// Object name space test (and 'ctx' sanity checks)
			if (base.Exists(ctx) == false)
				return (false);

			return ((mSupportVertexArray != true) || (Gl.IsVertexArray(ObjectName)));
		}

		/// <summary>
		/// Determine whether this object requires a name bound to a context or not.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for creating this object name.
		/// </param>
		/// <returns>
		/// It returns a boolean value indicating whether this RenderResource implementation requires a name
		/// generation on creation. In the case this routine returns true, the routine <see cref="CreateName"/>
		/// will be called (and it must be overriden). In  the case this routine returns false, the routine
		/// <see cref="CreateName"/> won't be called (and indeed it is not necessary to override it) and a
		/// name is generated automatically in a context-independent manner.
		/// </returns>
		protected override bool RequiresName(RenderContext ctx)
		{
			return (ctx.Caps.VertexArrayObject.Supported);
		}

		/// <summary>
		/// Create a BufferObject name.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for creating this object name.
		/// </param>
		/// <returns>
		/// It returns a valid object name for this BufferObject.
		/// </returns>
		protected override uint CreateName(RenderContext ctx)
		{
			// Determine vertex array support
			mSupportVertexArray = true;

			uint name;

			// Create buffer object
			Gl.GenVertexArrays(1, out name);
			RenderException.DebugCheckErrors();

			return (name);
		}

		/// <summary>
		/// Delete a BufferObject name.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for deleting this object name.
		/// </param>
		/// <param name="name">
		/// A <see cref="System.UInt32"/> that specifies the object name to delete.
		/// </param>
		protected override void DeleteName(RenderContext ctx, uint name)
		{
			if (mSupportVertexArray) {
				// Delete buffer object
				Gl.DeleteVertexArrays(1, new uint[] {name});
				RenderException.DebugCheckErrors();
			}
		}

		/// <summary>
		/// Actually create this RenderResource resources.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for allocating resources.
		/// </param>
		protected override void CreateObject(RenderContext ctx)
		{
			// Validate vertex array object
			Validate();

			// Create vertex arrays
			foreach (VertexArray inputArray in mVertexArrays.Values) {
				if (inputArray.Array.Exists(ctx) == false)
					inputArray.Array.Create(ctx);
			}
			// Create element array
			foreach (AttributeElements attributeElements in Elements) {
				if (attributeElements.ArrayIndices != null)
					attributeElements.ArrayIndices.Create(ctx);	
			}
			// Create shader program, if defined
			if (Shader != null)
				Shader.Create(ctx);
		}

		/// <summary>
		/// Flag indicating whether used context really support vertex arrays.
		/// </summary>
		private bool mSupportVertexArray;

		#endregion
	}
}
